package com.cocolover2.andbase;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;

import com.cocolover2.andbase.utils.MLogger;

import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;

/**
 * 全局异常捕获
 * <br>
 * 异常日志文件路径:/mnt/sdcard/crash_log/
 * Created by liubo on 7/26/16.
 */
public class AndBaseCrashHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "AndBaseCrashHandler";
    private static final Boolean DEBUG = true;
    //异常日志文件夹
    private static String PATH = Environment.getExternalStorageDirectory().getPath() + "/crash_log/";

    private static final String FILE_NAME = "crash_";
    private static final String FILE_NAME_SUFFIX = ".trace";

    private static AndBaseCrashHandler sInstance;
    private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    private Context mContext;
    private File mCrashFile;

    private String mActionUrl = "";
    private String mFileKey = "";


    private AndBaseCrashHandler() {
    }
    //获取AndBaseCrashHandler实例
    public static AndBaseCrashHandler getInstance() {
        if (sInstance == null) {
            synchronized (AndBaseCrashHandler.class) {
                if (sInstance == null)
                    sInstance = new AndBaseCrashHandler();
            }
        }
        return sInstance;
    }

    /**
     * 初始化异常捕获
     * @param context   上下文环境
     * @param actionUrl 文件上传路径
     * @param fileKey   文件key值
     */
    public void init(Context context, String actionUrl, String fileKey) {
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
        mContext = context.getApplicationContext();
        mActionUrl = actionUrl;
        mFileKey = fileKey;
    }

    /**
     * 这个是关键函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法
     *
     * @param thread 出现未捕获异常的线程
     * @param ex     未捕获的异常
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        try {
            // 导出异常信息到SD卡
            dumpExceptionToSDCard(ex);
            //上传异常到服务器
            new Thread(new Runnable() {
                @Override
                public void run() {
                    uploadExceptionToServer();
                }
            }).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        ex.printStackTrace();
        //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
        if (mDefaultCrashHandler != null) {
            mDefaultCrashHandler.uncaughtException(thread, ex);
        } else {
            Process.killProcess(Process.myPid());
            System.exit(0);
        }
    }


    private void dumpExceptionToSDCard(Throwable ex) throws IOException {
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            if (DEBUG)
                Log.w(TAG, "sdcard unmounted,sip dump exception");
        }

        File dir = new File(PATH);
        if (!dir.exists())
            dir.mkdirs();

        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date(System.currentTimeMillis()));
        mCrashFile = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);

        PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(mCrashFile)));
        pw.print("#Crash time: ");
        pw.println(time);
        try {
            dumpPhoneInfo(pw);
            pw.println("<--------------------------------->");
            ex.printStackTrace(pw);
            pw.close();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

    }

    private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
        pw.print("# App version: ");
        pw.println(pi.versionName);
        pw.print("# Version code: ");
        pw.println(pi.versionCode);

        //android 版本号
        pw.print("# OS Version: ");
        pw.println(Build.VERSION.RELEASE);
        pw.print("# SDK Version: ");
        pw.println(Build.VERSION.SDK_INT);

        //手机制造商
        pw.print("# Vendor: ");
        pw.println(Build.MANUFACTURER);

        //手机型号
        pw.print("# Model: ");
        pw.println(Build.MODEL);

        //CPU架构

        pw.print("# CPU ABI: ");
        if (Build.VERSION.SDK_INT > 21)
            pw.println(Arrays.toString(Build.SUPPORTED_ABIS));
        else
            pw.println(Build.CPU_ABI);

    }


    /* 上传文件至Server的方法 */
    private void uploadExceptionToServer() {
        if (TextUtils.isEmpty(mActionUrl) || mFileKey == null) {
            MLogger.e(TAG, "异常日志的上传路径为空或者文件key值为空,请修正");
            return;
        }

        String end = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        try {
            URL url = new URL(mActionUrl);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            /* 允许Input、Output，不使用Cache */
            con.setDoInput(true);
            con.setDoOutput(true);
            con.setUseCaches(false);
            /* 设置传送的method=POST */
            con.setRequestMethod("POST");
            /* setRequestProperty */
            con.setRequestProperty("Connection", "Keep-Alive");
            con.setRequestProperty("Charset", "UTF-8");
            con.setRequestProperty("Content-Type",
                    "multipart/form-data;boundary=" + boundary);
            /* 设置DataOutputStream */
            DataOutputStream ds = new DataOutputStream(con.getOutputStream());
            ds.writeBytes(twoHyphens + boundary + end);
            ds.writeBytes("Content-Disposition: form-data; " + "name=\"" + mFileKey + "\";filename=\"" + mCrashFile.getName() + "\"" + end);
            ds.writeBytes(end);
            /* 取得文件的FileInputStream */
            FileInputStream fStream = new FileInputStream(mCrashFile);
            /* 设置每次写入1024bytes */
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = -1;
            /* 从文件读取数据至缓冲区 */
            while ((length = fStream.read(buffer)) != -1) {
                /* 将资料写入DataOutputStream中 */
                ds.write(buffer, 0, length);
            }
            ds.writeBytes(end);
            ds.writeBytes(twoHyphens + boundary + twoHyphens + end);
            /* close streams */
            fStream.close();
            ds.flush();
            /* 取得Response内容 */
            if (con.getResponseCode() == 200) {
                InputStream is = con.getInputStream();
                int ch;
                StringBuffer b = new StringBuffer();
                while ((ch = is.read()) != -1) {
                    b.append((char) ch);
                }
                MLogger.e("------异常日志上传成功--------" + b.toString());
            } else {
                MLogger.e("---------------上传失败-----------------");
            }

            ds.close();
        } catch (Exception e) {
            MLogger.e("---------------上传失败-----------------");
        }
    }

}
